﻿//-----------------------------------------------------------------------
// <copyright file="ShortcutIO.cs" author="Lennart">
//     License: MIT
// </copyright>
//-----------------------------------------------------------------------
namespace UtilityMethods {
    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using IWshRuntimeLibrary;
    using Microsoft.Win32;
    using Shell32;

    /// <summary>
    /// Provides functions for creating and changing shortcuts.
    /// </summary>
    public static class ShortcutIO {
        /// <summary>
        /// Handle required for various shortcut operations.
        /// </summary>
        private static WshShellClass scriptingHostHandle = new WshShellClass();

        /// <summary>
        /// Changes the icon of a shortcut.
        /// </summary>
        /// <param name="iconFile">The new icon.</param>
        /// <param name="target">The shortcut that should have its icon changed.</param>
        public static void SetShortcutIcon(string iconFile, string target) {
            Shell shellHandler = new Shell();
            Shell32.Folder folder = shellHandler.NameSpace(Path.GetDirectoryName(target));
            FolderItem folderItem = folder.ParseName(Path.GetFileName(target));
            if (folderItem != null) {
                ShellLinkObject shortcutTarget = (Shell32.ShellLinkObject)folderItem.GetLink;
                Debug.WriteLine(shortcutTarget.Path.ToString());
                shortcutTarget.SetIconLocation(iconFile, 0);
                shortcutTarget.Save();
            }
        }

        /// <summary>
        /// Changes the icon of a shortcut.
        /// </summary>        
        /// <param name="target">The shortcut that should have its icon changed.</param>
        /// <param name="dllFile">The full path to a DLL file.</param>
        /// <param name="index">The index of the icon within the DLL file.</param>
        public static void SetShortcutIcon(string target, string dllFile, int index) {
            Shell shellHandler = new Shell();
            Shell32.Folder folder = shellHandler.NameSpace(Path.GetDirectoryName(target));
            FolderItem folderItem = folder.ParseName(Path.GetFileName(target));
            if (folderItem != null) {
                ShellLinkObject shortcutTarget = (Shell32.ShellLinkObject)folderItem.GetLink;
                Debug.WriteLine(shortcutTarget.Path.ToString());
                shortcutTarget.SetIconLocation(dllFile, index);
                shortcutTarget.Save();
            }
        }

        /// <summary>
        /// Creates a shortcut (*.lnk) to a given file.
        /// </summary>
        /// <param name="targetFile">The file the shortcut points to.</param>
        /// <param name="targetDirectory">The working directory of the shortcut.</param>        
        /// <param name="destinationPath">The directory where the shortcut will be created.</param>   
        /// <exception cref="UnauthorizedAccessException">Thrown when the the user has insufficient access rights to the destination path.</exception>
        public static void MakeShortcut(string targetFile, string targetDirectory, string destinationPath) {
            destinationPath = Environment.ExpandEnvironmentVariables(destinationPath);
            IWshShortcut shortcut = (IWshShortcut)scriptingHostHandle.CreateShortcut(destinationPath + @"\" + FilesystemIO.GetFileDescription(targetFile, targetDirectory, true) + @".lnk");
            Debug.WriteLine("Path: " + targetDirectory + "\\" + targetFile + ".lnk");

            shortcut.TargetPath = targetDirectory + "\\" + targetFile;
            shortcut.Description = targetFile;

            try {
                shortcut.Save();
            }
            catch (UnauthorizedAccessException) {
                throw;
            }
        }

        /// <summary>
        /// Creates a shortcut (*.lnk) to a given file.
        /// </summary>
        /// <param name="targetFile">The file the shortcut links to.</param>
        /// <param name="targetDirectory">The working directory of the shortcut.</param>  
        /// <param name="destinationPath">The path where the shortcut is saved to.</param>  
        /// <param name="description">The shortcut description/title.</param>
        /// <exception cref="UnauthorizedAccessException">Thrown when the the user has insufficient access rights to the destination path.</exception>
        public static void MakeShortcut(string targetFile, string targetDirectory, string destinationPath, string description) {
            destinationPath = Environment.ExpandEnvironmentVariables(destinationPath);
            string fileDescription;

            if (!description.Equals(FilesystemIO.GetFileDescription(targetFile, targetDirectory, false))) {
                fileDescription = description;
            }
            else {
                fileDescription = FilesystemIO.GetFileDescription(targetFile, targetDirectory, true);
            }

            IWshShortcut shortcut = (IWshShortcut)scriptingHostHandle.CreateShortcut(destinationPath + @"\" + fileDescription + @".lnk");
            Debug.WriteLine("Path: " + targetDirectory + "\\" + targetFile + ".lnk");

            shortcut.TargetPath = targetDirectory + "\\" + targetFile;
            shortcut.Description = targetFile;

            try {
                shortcut.Save();
            }
            catch (UnauthorizedAccessException) {
                throw;
            }
        }

        /// <summary>
        /// Creates a fake URL shortcut by running the systems' default browser with the actual URL as Argument.
        /// </summary>
        /// <param name="url">The URL the link should point to.</param>
        /// <param name="name">The name of the shortcut.</param>
        /// <param name="iconFile">The icon.</param>
        /// <param name="targetPath">The path where the shortcut will be created.</param>
        /// <exception cref="UnauthorizedAccessException">Thrown when the the user has insufficient access rights to the destination path.</exception>
        public static void MakeURLShortcut(string url, string name, string iconFile, string targetPath) {
            IWshShortcut shortcut = (IWshShortcut)scriptingHostHandle.CreateShortcut(targetPath + @"\" + name + @".lnk");

            // Get ProgID from default HTTP handler
            RegistryKey defaultHttpAssociation = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice");
            string defaultBrowserProgId = defaultHttpAssociation.GetValue("ProgId").ToString();

            // Search the ProgID for the actual browser command
            RegistryKey defaultBrowserRegKey = Registry.ClassesRoot.OpenSubKey(defaultBrowserProgId + @"\shell\open\command");
            string defaultBrowserCommand = defaultBrowserRegKey.GetValue(string.Empty).ToString();

            // Format command string
            defaultBrowserCommand = defaultBrowserCommand.Replace("\"", string.Empty);
            defaultBrowserCommand = defaultBrowserCommand.Replace("%1", string.Empty);
            defaultBrowserCommand = defaultBrowserCommand.Replace("-", string.Empty);
            defaultBrowserCommand = defaultBrowserCommand.Trim();

            shortcut.TargetPath = defaultBrowserCommand;
            shortcut.Arguments = url;

            try {
                shortcut.Save();
                if (!iconFile.Equals("none")) {
                    SetShortcutIcon(iconFile, targetPath + @"\" + name + @".lnk");
                }
            }
            catch (UnauthorizedAccessException) {
                Debug.WriteLine("UAE LINK");
                throw;
            }
        }

        /// <summary>
        /// Invokes the icon selector dialogue for *.dll files. Use with GetShell32Icon(int).
        /// </summary>
        /// <param name="handle">Main form handle.</param>
        /// <param name="dllPath">The full path to a DLL file.</param>
        /// <returns>int.MaxValue if no icon was selected, or the index of the icon in the file.</returns>
        public static int ShowShellIconDialog(IntPtr handle, string dllPath) {
            int iconIndex = int.MaxValue;
            StringBuilder shellPath = new System.Text.StringBuilder(dllPath, 500);
            int retval = PickIconDlg(handle, shellPath, shellPath.Length + 1, ref iconIndex);

            return iconIndex;
        }

        /// <summary>
        /// Retrieves an icon from a *.dll file. Use with ShowShellIconDialog(IntPtr).
        /// </summary>
        /// <param name="dllFile">The full path to a DLL file.</param>
        /// <param name="index">The index of the icon inside the file.</param>
        /// <returns>The selected icon.</returns>
        public static Icon GetDllIcon(string dllFile, int index) {
            IntPtr iconIndex = ExtractIcon(IntPtr.Zero, dllFile, index);
            Icon shellIcon = System.Drawing.Icon.FromHandle(iconIndex);
            return shellIcon;
        }

        /// <summary>
        /// Renames a shortcut by moving it.
        /// </summary>
        /// <param name="pathToShortcut">Path to the shortcut that will be renamed.</param>
        /// <param name="newName">The new name for the shortcut.</param>
        public static void SetShortcutName(string pathToShortcut, string newName) {
            if (System.IO.File.Exists(pathToShortcut)) {
                System.IO.File.Move(pathToShortcut, Path.Combine(Path.GetDirectoryName(pathToShortcut), newName) + ".lnk");
            }
        }

        #region NativeMethods
        /// <summary>
        /// Opens a dialog to pick an icon from a dll or exe file.
        /// </summary>
        /// <param name="windowHandle">Handle to the window.</param>
        /// <param name="pathToFile">Path to the dll or exe file to open.</param>
        /// <param name="pathLength">Length of the path.</param>
        /// <param name="returnedIndex">The int variable that gets the returned icon index.</param>
        /// <returns>1 if successful, otherwise 0.</returns>
        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        public static extern int PickIconDlg(IntPtr windowHandle, System.Text.StringBuilder pathToFile, int pathLength, ref int returnedIndex);

        /// <summary>
        /// Extracts an icon from a dll or exe file.
        /// </summary>
        /// <param name="instanceHandle">Instance to the application calling the method. Can be IntPtr.Zero.</param>
        /// <param name="pathToFile">Full path to the exe, dll or ico file.</param>
        /// <param name="index">The index of the icon in the file. Use values >-1 for the index of the icon, or -1 for the total number of icons in the file.</param>
        /// <returns>A handle to the selected icon. Use Icon.FromHandle() to get an Icon object.</returns>
        [DllImport("shell32.dll")]
        public static extern IntPtr ExtractIcon(IntPtr instanceHandle, string pathToFile, int index);
        #endregion
    }
}
